#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<sys/epoll.h>
#include<ctype.h>
#include<arpa/inet.h>
//epoll_wait 监听对象有2类：lfd和cfd
int main(void)
{
    int lfd;//监听套接字
    char buf[1024];
    int i;
    int cfd ;//client连接套接字
    struct sockaddr_in serv_addr,clit_addr;
    socklen_t clit_len;
    struct epoll_event temp,ep[1024];

    //设置监听套接字
    lfd=socket(AF_INET,SOCK_STREAM,0);
    //端口复用
    int opt=1;
    setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    //初始化sockaddr_in定义变量
    memset(&serv_addr,0,sizeof(serv_addr));

    //初始化serv_addr的结构
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_port=htons(4531);
    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);

    //bind绑定，将监听套接字lfd和serv_addr进行绑定
    bind(lfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
    //设置监听上限
    listen(lfd,128);

    //创建红黑树的根节点
    ssize_t efd;
    efd=epoll_create(1024);//size 1024,创建红黑树，监听的文件描述符数量上限参考值
    //在红黑树上，设置监听lfd 的读事件
    //初始化lfd在红黑树的监听的读事件信息
    temp.events=EPOLLIN;
    temp.data.fd=lfd;

    //将lfd的节点信息挂在红黑树上
    epoll_ctl(efd,EPOLL_CTL_ADD,lfd,&temp);
    
    char str[1024];
    
    int realfd;
    for(;;)
    {
       realfd= epoll_wait(efd,ep,1024,-1);
        
       for(i=0;i<realfd;i++)
       {
           //判断是否是读事件，若不是，则继续
           if(!(ep[i].events&EPOLLIN))
           {
               continue;
           }

           //监听到client有connect的请求，既监听到文件描述符上有lfd的读事件发生
           if(ep[i].data.fd==lfd)
           {
                clit_len=sizeof(clit_addr);
                cfd=accept(lfd,(struct sockaddr*)&clit_addr,&clit_len);
                printf("receive from %s at port %d\n",inet_ntop(AF_INET,&clit_addr.sin_addr,str,sizeof(str)),ntohs(clit_addr.sin_port));

                //将连接的client端的cfd， 设置在红黑树上，监听是否有cfd的读事件发生
                temp.events=EPOLLIN;
                temp.data.fd=cfd;
                epoll_ctl(efd,EPOLL_CTL_ADD,cfd,&temp);

           }else
           {
               //监听到有client 发出读事件的请求
               int tempfd=ep[i].data.fd;
               int readcount=read(tempfd,buf,sizeof(buf));
               if(readcount==0)
               {
                   //说明client关闭了写端,客户端停止监听
                   //将该文件描述符对应的监听事件从红黑树上移除
                   epoll_ctl(efd,EPOLL_CTL_DEL,tempfd,NULL);
                   close(tempfd);
               }else if(readcount<-1)
               {
                    perror("read fail!\n");
                    epoll_ctl(efd,EPOLL_CTL_DEL,tempfd,NULL);
                    close(tempfd);
               }else
               {
                   for(int i=0;i<readcount;i++)
                   {
                       buf[i]=toupper(buf[i]);
                   }
                   write(tempfd,buf,readcount);
                   write(STDOUT_FILENO,buf,readcount);
               }


           }
       }
    }


    close(lfd);







    return 0;
}
